package router

import (
	"container/list"
	"container/ring"
	"crypto/md5"
	"errors"
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
	"xin-job/admin/abiz"
	"xin-job/core/biz"
)

// Router 路由策略/负载均衡策略
type Router interface {
	// router 路由
	// addressList 执行器地址
	// jobId 任务ID
	Router(jobId int, addressList []string) (address string, err error)
	GetTitle() string // 获取策略名称
}

// RouteFirst 第一个
type RouterFirst struct{}

func (r *RouterFirst) Router(jobId int, addressList []string) (address string, err error) {
	return addressList[0], nil
}
func (r *RouterFirst) GetTitle() string {
	return "第一个"
}

// RouteLast 最后一个
type RouterLast struct{}

func (r *RouterLast) Router(jobId int, addressList []string) (address string, err error) {
	return addressList[len(addressList)-1], nil
}
func (r *RouterLast) GetTitle() string {
	return "最后一个"
}

// RouteRandom 随机一个
type RouterRandom struct{}

func (r *RouterRandom) Router(jobId int, addressList []string) (address string, err error) {
	i := rand.Intn(len(addressList))
	return addressList[i], nil
}
func (r *RouterRandom) GetTitle() string {
	return "随机"
}

// RouteRound 轮询
type RouterRound struct {
	cache            sync.Map
	cache_valid_time int64
}

func (r *RouterRound) Router(jobId int, addressList []string) (address string, err error) {
	// 最简单的轮询算法，其实很简答
	// num := atomic.Int64
	// i := num % len(addressList)  取余算法，i 会从 0 到 addressList 的 len 不断的循环
	// 上面的算法有个问题，就是所有的任务都是通用这一套逻辑。
	// 假设有两个任务 A 和 B，addressList 一共有个 2 个。
	// A 先调度，address := addressList[0]
	// B 跟着被调度， address := addressList[1]
	// A 在调度，A 还在索引为 0 的执行器上。

	// 所以正确的情况应该是 任务 A，在两个执行器上轮询。
	if len(addressList) == 0 {
		return "", errors.New("addressList is empty")
	}

	return addressList[r.count(jobId)%len(addressList)], nil
}
func (r *RouterRound) GetTitle() string {
	return "轮询"
}
func (r *RouterRound) count(jobId int) int {
	if time.Now().UnixMilli() > r.cache_valid_time {
		r.cache.Range(func(key, value any) bool {
			r.cache.Delete(key)
			return true
		})
		// 缓存一天
		r.cache_valid_time = time.Now().UnixMilli() + 1000*60*60*24
	}

	// 使用一个并发安全的 map 缓存，每个 job 的路由次数。

	count, ok := r.cache.Load(jobId)
	if ok {
		if v, ok := count.(*atomic.Uint64); ok {
			// 100000 大于这个次数，xxl-job 里面这么写的。不太理解
			// 猜测，数太大取余数性能下降？
			if v.Load() > 1000000 {
				i := rand.Int63n(100)
				v.Store(uint64(i))
			} else {
				v.Add(1)
			}
			return int(v.Load())
		}
	}

	// 赋值随机数
	// 假设有 100 个任务，其中有一些比较耗时。
	// 第一次执行任务时，100 个任务都会在第一个执行器执行。如果耗时任务太多对一台执行器服务压力大。
	// 设置随机数，保证一定的随机性。
	c := &atomic.Uint64{}
	i := rand.Int63n(100)
	c.Store(uint64(i))
	r.cache.Load(c)
	return int(c.Load())
}

// RouteLRU Least Recently Used 最近最少使用
type RouterLRU struct {
	cache            sync.Map // jobId:list
	cache_valid_time int64
}

func (r *RouterLRU) Router(jobId int, addressList []string) (address string, err error) {
	if time.Now().UnixMilli() > r.cache_valid_time {
		r.cache.Range(func(key, value any) bool {
			r.cache.Delete(key)
			return true
		})
		// 缓存一天
		r.cache_valid_time = time.Now().UnixMilli() + 1000*60*60*24
	}

	// 所有的 address 放入缓存
	// 第一次触发 router 则添加所有
	// 第二次触发 router 则添加新增加的执行器服务器地址
	for _, address := range addressList {
		if !r.itemContainsVal(jobId, address) {
			r.put(jobId, address)
		}
	}

	// del 已经不存在的 address
	l := r.getCacheValue(jobId)
	if l == nil {
		return "", errors.New("lru err")
	}

	for e := l.Front(); e != nil; e = e.Next() {
		if s, ok := e.Value.(string); ok {
			if !contains(addressList, s) {
				l.Remove(e)
			}
		}
	}

	address = r.next(jobId)

	return
}
func (r *RouterLRU) GetTitle() string {
	return "最近最久未使用"
}
func contains(addressList []string, value string) bool {
	for _, v := range addressList {
		if v == value {
			return true
		}
	}

	return false
}

// put 放入
func (r *RouterLRU) put(key int, value string) {
	l, ok := r.cache.Load(key)
	if ok {
		if v, ok := l.(*list.List); ok {
			v.PushBack(value)
		}
	} else {
		ll := list.New()
		ll.PushBack(value)
		r.cache.Store(key, ll)
	}
}

// next 获取首个元素，并将该元素放入尾部
func (r *RouterLRU) next(key int) string {
	l := r.getCacheValue(key)
	if l == nil {
		return ""
	}

	// 获得首个元素
	f := l.Front()

	if s, ok := f.Value.(string); ok {
		if ok {
			// 将该元素移动到尾部
			l.MoveToBack(f)
			return s
		}
	}

	return ""
}

// cache list 是否包含 address
func (r *RouterLRU) itemContainsVal(key int, value string) bool {
	l := r.getCacheValue(key)
	if l == nil {
		return false
	}

	for e := l.Front(); e != nil; e = e.Next() {
		if s, ok := e.Value.(string); ok {
			if s == value {
				return true
			}
		}
	}

	return false
}

func (r *RouterLRU) getCacheValue(key int) *list.List {
	v, ok := r.cache.Load(key)
	if !ok {
		return nil
	}

	if l, ok := v.(*list.List); ok {
		return l
	}
	return nil
}

// RouteLFU Least Frequently Used 最不经常使用
type RouterLFU struct {
	cache            sync.Map // jobId: map[address]num
	cache_valid_time int64
}

func (r *RouterLFU) Router(jobId int, addressList []string) (address string, err error) {
	if time.Now().UnixMilli() > r.cache_valid_time {
		r.cache.Range(func(key, value any) bool {
			r.cache.Delete(key)
			return true
		})
		// 缓存一天
		r.cache_valid_time = time.Now().UnixMilli() + 1000*60*60*24
	}

	v, ok := r.cache.Load(jobId)
	if !ok {
		v = make(map[string]int, len(addressList))
		r.cache.Store(jobId, v)
	}

	m, ok := v.(map[string]int)
	if !ok {
		m = make(map[string]int, len(addressList))
		r.cache.Store(jobId, m)
	}

	for _, address := range addressList {
		num, ok := m[address]
		if !ok || num > 1000000 {
			// 不存在次数以及超过 1000000 则初始化次数
			// 随机一个数。减轻服务器压力。
			// 与 RouterRound 一个道理，如果 num 都为 1，那么多个任务可能全部都调度到一台执行器。
			m[address] = rand.Intn(len(addressList))
		}
	}

	// 删除过期执行器
	for k := range m {
		if !contains(addressList, k) {
			delete(m, k)
		}
	}

	// map[address]num 根据 num 排序，取出 num 最小的数据
	type kv struct {
		key   string
		value int
	}

	var temp []kv
	for k, v := range m {
		temp = append(temp, kv{k, v})
	}
	// 排序
	sort.Slice(temp, func(i, j int) bool {
		return temp[i].value < temp[j].value
	})

	// 第一个就是使用次数最少的
	fist := temp[0]

	m[fist.key]++

	return fist.key, nil
}

func (r *RouterLFU) GetTitle() string {
	return "最不经常使用"
}

// RouterBusyover 忙碌转移
type RouterBusyover struct{}

func (r *RouterBusyover) Router(jobId int, addressList []string) (string, error) {

	for _, address := range addressList {
		c := abiz.GetExecutorBizClient()
		c.ClientAddressUrl = address
		r := c.IdleBeat(&biz.IdleBeatParam{JobId: jobId})
		if r.Code == 200 {
			return address, nil
		}
	}

	return "", fmt.Errorf("address list is busyover")
}

func (r *RouterBusyover) GetTitle() string {
	return "忙碌转移"
}

// RouterFailover 故障转移
type RouterFailover struct{}

func (r *RouterFailover) Router(jobId int, addressList []string) (string, error) {
	for _, address := range addressList {
		c := abiz.GetExecutorBizClient()
		c.ClientAddressUrl = address
		r := c.Beat()
		if r.Code == 200 {
			return address, nil
		}
	}
	return "", fmt.Errorf("address list is failover")
}

func (r *RouterFailover) GetTitle() string {
	return "故障转移"
}

// RouterHash 一致Hash
type RouterHash struct{}

func (r *RouterHash) Router(jobId int, addressList []string) (address string, err error) {
	// 环形链表
	rring := ring.New(int(len(addressList)))

	for _, v := range addressList {
		key := r.hash(v)
		m := map[int64]string{key: v}
		rring.Value = m
		rring = rring.Next()
	}

	jobHash := r.hash(strconv.Itoa(jobId))

	rring.Do(func(a any) {
		if v, ok := a.(map[int64]string); ok {
			for hashId, a := range v {
				if hashId > jobHash {
					address = a // 返回第一个大于 jobId 的 address
					return
				}
			}
		}
	})

	// address 为空，说明 ring 中没有比 jobHash 更大的。
	// 此时则取第一个即可
	if address == "" {
		address = addressList[0]
	}

	return
}

func (r *RouterHash) GetTitle() string {
	return "一致性HASH"
}

func (r *RouterHash) hash(v string) int64 {
	m := md5.New()
	m.Write([]byte(v))
	s := m.Sum(nil)

	return int64(s[3]&0xFF<<24|s[2]&0xFF<<16|s[1]&0xFF<<8|s[0]&0xFF) & 0xFFFFFFFF
}

const (
	FIRST                 = "FIRST"                 // 第一个
	LAST                  = "LAST"                  // 最后一个
	ROUND                 = "ROUND"                 // 随机
	RANDOM                = "RANDOM"                // 轮询
	CONSISTENT_HASH       = "CONSISTENT_HASH"       // 一致HASH
	LEAST_FREQUENTLY_USED = "LEAST_FREQUENTLY_USED" // 最不经常使用
	LEAST_RECENTLY_USED   = "LEAST_RECENTLY_USED"   // 最近最久未使用
	FAILOVER              = "FAILOVER"              // 故障转移
	BUSYOVER              = "BUSYOVER"              // 忙碌转移
	SHARDING_BROADCAST    = "SHARDING_BROADCAST"    // 分片广播
)

type RouterSingleton struct {
	m map[string]Router
}

func (rs *RouterSingleton) GetRouter(routerName string) (Router, error) {
	router, ok := rs.m[routerName]
	if ok {
		return router, nil
	}

	return nil, fmt.Errorf("routerName=%s router not found", routerName)
}

var rs *RouterSingleton

func GetRouter(key string) Router {
	r, ok := rs.m[key]
	if ok {
		return r
	}
	return rs.m[ROUND] // 默认轮询
}

func init() {
	rs = &RouterSingleton{
		m: make(map[string]Router, 10),
	}

	rs.m[FIRST] = &RouterFirst{}               // 第一个
	rs.m[LAST] = &RouterLast{}                 // 最后一个
	rs.m[ROUND] = &RouterRound{}               // 随机
	rs.m[RANDOM] = &RouterRandom{}             // 轮询
	rs.m[CONSISTENT_HASH] = &RouterHash{}      // 一致HASH
	rs.m[LEAST_FREQUENTLY_USED] = &RouterLFU{} // LFU
	rs.m[LEAST_RECENTLY_USED] = &RouterLRU{}   // LRU
	rs.m[FAILOVER] = &RouterFailover{}         // 故障转移
	rs.m[BUSYOVER] = &RouterBusyover{}         // 忙碌转移
}
